//! Система управления приложением (сценой)
class System{
protected: // Устройства

	//! Состояние всяких девайсов (клава, мыш, экран ...)
	static Device _device;

public:

	//! Вернет состояние устройств на чтение
	static const Device& device();
	
protected: // GLFW

	//! Псоледнее значение вернутое прокруткой колеса мыши
	//* glfw возвращает вращение колеса в виде возрастающего или уменьшающегося
	//* значения, чтобы понять куда оно крутилось надо помнить его прошлое положение
	static int _last_mouse_wheel_pos;

	//! Обработчик glfw события нажатия на клавишу мыши
	static void GLFWCALL _mouse_button(int button, int action);

	//! Обработчик glfw события движения мыши
	static void GLFWCALL _mouse_pos(int x, int y);

	//! Обработчик glfw события движения колеса мыши
	static void GLFWCALL _mouse_wheel(int pos);

	//! Обработчик glfw события печать текста
	static void GLFWCALL _char(int ch, int action);

	//! Обработчик glfw события нажатия клавиши
	static void GLFWCALL _key(int key, int action);

	//! Обработчик glfw события обновления экрана
	static void GLFWCALL _refresh(void);
	
	//! Обработчик glfw события закрытия окна
	static int GLFWCALL _close_window(void);

	//! Обработчик glfw события изменение размера окна
	static void GLFWCALL _resize_window(int width,int height);
	
protected:

	static bool _initialized;
	static std::stack <Scene*> _scene_stack;
	
public:

	static int error_code;
	
	static void play(Scene* scene);
	
	//! Конструктор
	System();
	
	//! Деструктор
	~System();
	
public:

	//! Создаст основной объект типа system (инициализирует систему)
	static System system;
	
};

System System::system;

const Device& System::device(){
	return _device;
}

Device System::_device;

int System::_last_mouse_wheel_pos;

void GLFWCALL System::_mouse_button(int button, int action){
	event.push(Message(GE_MESSAGE_MOUSE_BUTTON,button,action));
}

void GLFWCALL System::_mouse_pos(int x, int y){
	event.push(Message(GE_MESSAGE_MOUSE_MOVE,x,y));
}

void GLFWCALL System::_mouse_wheel(int pos){
	if (pos>_last_mouse_wheel_pos)
		event.push(Message(GE_MESSAGE_MOUSE_WHEEL,1));
	if (pos<_last_mouse_wheel_pos)
		event.push(Message(GE_MESSAGE_MOUSE_WHEEL,0));
	_last_mouse_wheel_pos = pos;
}

void GLFWCALL System::_char(int ch, int action){
	event.push(Message(GE_MESSAGE_CHAR,ch,action));
}

void GLFWCALL System::_key(int key, int action){
	event.push(Message(GE_MESSAGE_KEY,key,action));
}

void GLFWCALL System::_refresh(void){
	event.push(Message(GE_MESSAGE_REDRAW));
}

int GLFWCALL System::_close_window(void){
	event.push(Message(GE_MESSAGE_QUIT));
	return GL_FALSE;
}

void GLFWCALL System::_resize_window(int width,int height){
	_device.screen.width = width;
	_device.screen.height = height;
	event.push(Message(GE_MESSAGE_RESIZE,width,height));
}

bool System::_initialized;

int System::error_code;

std::stack <Scene*> System::_scene_stack;

void System::play(Scene* scene){
	if (scene&&_initialized) {
		if (Scene::_active_scene) {
			event.push(Message(GE_MESSAGE_INIT,Scene::_active_scene));
		} else {
			Scene::_active_scene = scene;
			Scene::_active_scene->on_message(Message(GE_MESSAGE_INIT));
			bool redraw = false;
			bool resize = false;
			// fps
			unsigned int fps = 0;
			unsigned int fps_timer = clock() + 1000;
			// программный цикл
			while (Scene::_active_scene) {
				_device.timer.seconds = (float)clock()*0.001f;
				bool do_on_frame = true;
				int resize_width;
				int resize_height;
				if (event.empty())
					event.push(Message(GE_MESSAGE_NONE));
				while(!event.empty()){
					switch (event.front().type){
						case GE_MESSAGE_NONE:
							break;
						case GE_MESSAGE_KEY:
							if (event.front().value.as_int[0]<GE_KEYBOARD_MAX_KEY)
								_device.keyboard.key[event.front().value.as_int[0]] = event.front().value.as_int[1];
							if (Scene::_active_scene) Scene::_active_scene->on_message(event.front());
							break;
						case GE_MESSAGE_MOUSE_MOVE:
							_device.mouse.xrel = event.front().value.as_int[0] - _device.mouse.x;
							_device.mouse.yrel = event.front().value.as_int[1] - _device.mouse.y;
							_device.mouse.x = event.front().value.as_int[0];
							_device.mouse.y = event.front().value.as_int[1];
							if (Scene::_active_scene) Scene::_active_scene->on_message(event.front());
							break;
						case GE_MESSAGE_MOUSE_BUTTON:
							if (event.front().value.as_int[0]<GE_MOUSE_MAX_BUTTON)
								_device.mouse.button[event.front().value.as_int[0]] = event.front().value.as_int[1];
							if (Scene::_active_scene) Scene::_active_scene->on_message(event.front());
							break;
						case GE_MESSAGE_RESIZE:
							resize_width = event.front().value.as_int[0];
							resize_height = event.front().value.as_int[1];
							resize = true;
							break;
						case GE_MESSAGE_REDRAW:
							redraw = true;
							break;
						case GE_MESSAGE_INIT:
							if (event.front().value.as_void[0]) {
								Scene::_active_scene->on_message(Message(GE_MESSAGE_PAUSE));
								_scene_stack.push(Scene::_active_scene);
								Scene::_active_scene = (Scene*) event.front().value.as_void[0];
								Scene::_active_scene->on_message(Message(GE_MESSAGE_INIT));
							}
							break;
						case GE_MESSAGE_CLOSE:
							Scene::_active_scene->on_message(Message(GE_MESSAGE_CLOSE));
							if (!_scene_stack.empty()) {
								Scene::_active_scene = _scene_stack.top();
								_scene_stack.pop();
								Scene::_active_scene->on_message(Message(GE_MESSAGE_CONTINUE));
							} else {
								Scene::_active_scene = 0;
								while(!event.empty())
									event.pop();
							}
							break;
						case GE_MESSAGE_QUIT:
							Scene::_active_scene->on_message(Message(GE_MESSAGE_CLOSE));
							Scene::_active_scene=0;
							while(!_scene_stack.empty()){
								_scene_stack.top()->on_message(Message(GE_MESSAGE_CLOSE));
								_scene_stack.pop();
							}
							while(!event.empty())
								event.pop();
							break;
						default:
							Scene::_active_scene->on_message(event.front());
					}
					if (!event.empty()){
						event.pop();
						if (event.empty()&&do_on_frame){
							Scene::_active_scene->on_message(Message(GE_MESSAGE_ON_FRAME));
							do_on_frame = false;
						}
					}
				} // while event
				
				if (Scene::_active_scene) {
					if (resize) {
						Scene::_active_scene->on_message(Message(GE_MESSAGE_RESIZE,resize_width,resize_height));
						resize = false;
						redraw = true;
					}
					if (redraw) {
						Scene::_active_scene->on_message(GE_MESSAGE_REDRAW);
						glfwSwapBuffers();
						fps++;
						redraw = false;
					} else
						glfwPollEvents();
				}
				
				//fps calculation
				if ((unsigned int)clock() >= fps_timer){
					_device.timer.fps = fps;
					swprintf(_device.timer.fps_string,L"fps : %d",System::device().timer.fps);					
					fps = 0;
					fps_timer = clock() + 1000;
				}
				
			} // while scene
		}
	}
}	

//! Конструктор
System::System(){
	if (!_initialized) {
	
		if (glfwInit()==GL_FALSE) {
			error_code = 1;
			return;
		}
		
		glfwOpenWindowHint(GLFW_OPENGL_VERSION_MAJOR,3);
		glfwOpenWindowHint(GLFW_OPENGL_VERSION_MINOR,3);
		glfwOpenWindowHint(GLFW_OPENGL_FORWARD_COMPAT,GL_TRUE);
		glfwOpenWindowHint(GLFW_OPENGL_PROFILE,GLFW_OPENGL_CORE_PROFILE);
		if (glfwOpenWindow(_device.screen.width,_device.screen.height,8,8,8,8,32,8,GLFW_WINDOW)==GL_FALSE){
			error_code = 2;
			return; //glfwOpenWindow filed
		}
		
		glewExperimental=GL_TRUE;
		if (glewInit()!=GLEW_OK){
			error_code = 3;
			return; //glewInit filed
		}
	
		int rev;
		glfwGetGLVersion(&_device.opengl.version.major,&_device.opengl.version.minor,&rev);

		glfwEnable(GLFW_KEY_REPEAT);

		glfwSetWindowCloseCallback(_close_window);
		glfwSetWindowSizeCallback(_resize_window);
		glfwSetWindowRefreshCallback(_refresh);
		glfwSetKeyCallback(_key);
		glfwSetCharCallback(_char);
		glfwSetMouseButtonCallback(_mouse_button);
		glfwSetMousePosCallback(_mouse_pos);
		glfwSetMouseWheelCallback(_mouse_wheel);
		
		error_code = 0;
		_initialized = true;
		
	}
}
	
//! Деструктор
System::~System(){
	if (_initialized) {
		_initialized = false;
		glfwCloseWindow();
		glfwTerminate();
	}
}
